package org.lc.cmd.impl.bridge;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.lc.cmd.CmdDataBridge;
import org.lc.cmd.util.CmdOutUtil;
import org.lc.cmd.util.ProcessUtil;
import org.lc.cmd.util.StringUtil;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;

@Slf4j
public abstract class HttpClientBridge implements CmdDataBridge {
    /**
     * 服务器url
     * 格式：https://xxxx:xx
     */
    private final String serverUrl;

    public HttpClientBridge(String serverUrl) {
        this.serverUrl = serverUrl;
        initPoll();
    }

    public void connectServer(String serverUrl, String[] serverArgs, File logFile) {
        for (int i = 0; i < 5; i++) {
            try {
                connectServer();
                return;
            } catch (Exception e) {
                if (i == 0) {
                    // 首次连不上，可能是进程未启动，尝试启动进程
                    log.debug("启动后台进程中, 启动命令参数: {}", String.join(" ", serverArgs));
                    startServer(serverArgs, logFile);
                } else {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                        log.error("无法sleep", ex);
                    }
                }
            }
        }
        log.error("无法连接到后台进程: {}", serverUrl);
    }

    private void startServer(String[] args, File logFile) {
        String[] cmds = new String[args.length + 3];
        // 获取 Java 安装路径
        String javaHome = System.getProperty("java.home");

        // 获取 Java 可执行文件路径
        String javaExecutable = javaHome + File.separator + "bin" + File.separator + "java";

        // 获取当前 Java 程序路径
        String jarPath = HttpClientBridge.class.getProtectionDomain().getCodeSource().getLocation().getPath();

        // 构建完整的命令
        String[] command = {javaExecutable, "-jar", jarPath};
        System.arraycopy(command, 0,cmds, 0, command.length);
        System.arraycopy(args, 0, cmds, command.length, args.length);
        log.debug("启动后台进程，执行命令：{}", String.join(" ", cmds));
        ProcessUtil.executeInBackground(cmds, logFile);
    }

    private void connectServer() {
        send("/cmd/connect");
    }

    @Override
    public void sendCmdLine(String line) {
        if (StringUtils.hasText(line)) {
            send("/cmd/execute?line=" + line);
        }
    }

    @Override
    public void closeClient() {
        log.debug("客户端主动关闭");
        System.exit(0);
    }

    @Override
    public void closeServer() {
        send("/cmd/close_server");
    }

    private String send(String url) {
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(serverUrl + url).openConnection();
            connection.setRequestMethod("GET");
            connection.setUseCaches(false);
            connection.connect();
            int code = connection.getResponseCode();
            String resp = StreamUtils.copyToString(connection.getInputStream(), Charset.defaultCharset());
            if (code >= 200 && code < 300) {
                return resp;
            }
            log.error("响应错误, url: {}, code：{}, resp: {}", url, code, resp);
            return null;
        } catch (IOException e) {
            throw new RuntimeException("无法发送请求：" + url, e);
        }
    }

    private void initPoll() {
        Thread thread = new Thread(() -> {
            while (true) {
                String resp = send("/cmd/poll");
                if (StringUtil.isEmpty(resp)) {
                    continue;
                }
                PollModel poll = JSON.parseObject(resp, PollModel.class);
                switch (poll.getType()) {
                    case WRITE:
                        WriteModel writeModel = JSON.parseObject(poll.getData(), WriteModel.class);
                        write(writeModel.getStr(), writeModel.isAbove());
                        break;
                    case READ_LINE:
                        String prompt = poll.getData();
                        String line = readLine(prompt);
                        poll.setData(line);
                        send(String.format("%s/cmd/poll-back?type=%s&data=%s&id=%s", serverUrl, poll.getType(),
                                poll.getData(), poll.getId()));
                        break;
                    case CLOSE_CLIENT:
                        log.info("服务端通知关闭客户端");
                        System.exit(0);
                }
            }
        });
        thread.setDaemon(true);
        thread.start();
    }
}
